﻿using System;
using System.Collections.Generic;
using System.Data;
using AdaptiveAgents.Loggers;
using AdaptiveAgents.Agents;
using AdaptiveAgents.Games;
using AdaptiveAgents.Distributions;

namespace AdaptiveAgents.Experiments
{
    /// <summary>
    /// This experiment is created to see how fast dose the Adaptive agent (an agent using
    /// AdaptiveTeammateModel) is able to find out the epsilon of the other agents
    /// </summary>
    class EpsilonTestExperiment : Experiment
    {

        public EpsilonTestExperiment()
            : base(50, 3, 1) {}

        //data tables to save and print the data
        DataTable dt,dt1;

        /// <summary>
        /// Run the main experiment
        /// </summary>
        public override void runExperiment()
        {
            //data members - constants
            int GAMES = 100;
            int ROUNDS = 400;

            //data members - vars
            double competence; //adaptive agent
            double competence1; //other agents
            double competence2;

            double eps; //adaptive agent
            double eps1; //other agents
            double eps2;

            //rows to save the games data.
            DataRow row1,row2,row3,row4;
            
            //create columns for the data
            dt = new DataTable("SingleBlockExperiment");
            DataColumn cln = new DataColumn("GameNum", typeof(double));
            dt.Columns.Add(cln);
            cln = new DataColumn("Competence", typeof(double));
            dt.Columns.Add(cln);
            cln = new DataColumn("Competence1", typeof(double));
            dt.Columns.Add(cln);
            cln = new DataColumn("Competence2", typeof(double));
            dt.Columns.Add(cln);
            cln = new DataColumn("eps", typeof(double));
            dt.Columns.Add(cln);
            cln = new DataColumn("eps1", typeof(double));
            dt.Columns.Add(cln);
            cln = new DataColumn("eps2", typeof(double));
            dt.Columns.Add(cln);
            cln = new DataColumn("utility", typeof(double));
            dt.Columns.Add(cln);
            
            //create a column with the round number
            for (int i = 0; i < ROUNDS; ++i)
            {
                cln = new DataColumn(i.ToString(), typeof(double));
                dt.Columns.Add(cln);
            }

            dt1 = dt.Clone();

            //for each game
            for (int i = 0; i < GAMES; ++i)
            {
                //set new rows
                row1 = dt.NewRow();
                row2 = dt.NewRow();
                row3 = dt1.NewRow();
                row4 = dt1.NewRow();

                //create random epsilons for the agents
                eps = Generator.getNextDouble();
                eps1 = Generator.getNextDouble();
                eps2 = Generator.getNextDouble();

                //enter known data to rows
                row1["GameNum"] = i;
                row2["GameNum"] = i;
                row3["GameNum"] = i;
                row4["GameNum"] = i;

                row1["eps"] = eps;
                row1["eps1"] = eps1;
                row1["eps2"] = eps2;
                row2["eps"] = eps;
                row2["eps1"] = eps1;
                row2["eps2"] = eps2;
                row3["eps"] = eps;
                row3["eps1"] = eps1;
                row3["eps2"] = eps2;
                row4["eps"] = eps;
                row4["eps1"] = eps1;
                row4["eps2"] = eps2;

                // first let's set the environment
                Environment environment = new Environment();

                //Create a new population of players (diffrent epsilon for each agent)
                List<Agent> agents = generatePlayers(environment, eps, eps1, eps2, _logger);

                for (int j = 0; j < NumAgents; j++)
                {
                    Agent agent = agents[j];
                    environment.addAgent(agent);
                }

                //create game
                EpsilonTestGame game = (EpsilonTestGame)GameFactory.create(Games.GameType.EpsilonTest, environment, ROUNDS);

                double utility = 0;

                //create random competences for agents
                competence = Generator.getNextDouble();
                competence1 = Generator.getNextDouble();
                competence2 = Generator.getNextDouble();

                //enter known data to rows
                row1["competence"] = competence;
                row1["competence1"] = competence1;
                row1["competence2"] = competence2;
                row2["competence"] = competence;
                row2["competence1"] = competence1;
                row2["competence2"] = competence2;
                row3["competence"] = competence;
                row3["competence1"] = competence1;
                row3["competence2"] = competence2;
                row4["competence"] = competence;
                row4["competence1"] = competence1;
                row4["competence2"] = competence2;

                //set agents with their competence
                environment.agents[0].Competence = new Blocks(competence);
                environment.agents[1].Competence = new Blocks(competence1);
                environment.agents[2].Competence = new Blocks(competence2);

                //enter rows to game so the game will save there it's data
                game.Dr1 = row1;
                game.Dr2 = row2;
                game.PlayerRow = row3;
                game.Util = row4;

                //start the game. save the game utility coming back
                utility = game.start(_logger);

                //save the game utility
                row1["utility"] = utility;
                row2["utility"] = utility;

                //add data to dataTables
                dt.Rows.Add(row1);
                dt.Rows.Add(row2);

                dt1.Rows.Add(row3);
                dt1.Rows.Add(row4);
            }

            //save data to XMLs filse
            String stamp = DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss");
            dt.WriteXml("EpsilonTestExperiment_" + stamp + ".xml");
            dt1.WriteXml("EpsilonTestExperimentWithGameData_" + stamp + ".xml");
        }

        /// <summary>
        /// Create players accordig to parameters
        /// </summary>
        /// <param name="env">the environment</param>
        /// <param name="epsilon0">epsilon for adaptive agent</param>
        /// <param name="epsilon1">epsilon for 2nd agent</param>
        /// <param name="epsilon2">epsilon for 3rd agent</param>
        /// <param name="logger">logger</param>
        /// <returns>list of new agents created</returns>
        protected List<Agent> generatePlayers(Environment env, double epsilon0, double epsilon1, double epsilon2, Logger logger)
        {
            int numOfAgents = 3;
            List<Agent> retList = new List<Agent>(numOfAgents);

            AgentDirector director = new AgentDirector();

            director.Builder = new Agents.NormalBuilder();
            director.construct(0, env, epsilon0, logger);
            retList.Add(director.getAgent());

            director.Builder = new Agents.NormalBuilder();
            director.construct(1, env, epsilon1, logger);
            retList.Add(director.getAgent());

            director.Builder = new Agents.NormalBuilder();
            director.construct(2, env, epsilon2, logger);
            retList.Add(director.getAgent());

            return retList;
        }
    }
}
